home *** CD-ROM | disk | FTP | other *** search
- SMALL equ 0
- LARGE equ 1
-
- CODE_M equ LARGE ; Set this according to the code
- ; model being used
-
- DGROUP group NULL, _DATA
- NULL segment para public 'BEGDATA'
- NULL ENDS
-
- _DATA segment para public 'DATA'
- extrn _mode:word, _vidmseg:word, _bitspixl:word
-
- psetjump dw offset _TEXT:psetexit ; holds address of point-plotting
- ; routine called by pset()
- count dw 0 ; used by _line routine
- adj dw 0 ; used by _fline routine
- _DATA ends
-
- _TEXT segment para public 'CODE'
- assume cs:_TEXT, ds:DGROUP
-
- public _pset, _init160, _setpjmp, _line, _fline, _doint
-
- ; The following routine plots a point on the graphics screen in the
- ; specified color:
-
- ; returns bx=video offset, dh=mask value, dl=shifted color value
- ; (see below); these values are used by the fline() function
-
- IF CODE_M
- _pset proc far ; pset(x, y, color)
-
- xcoord equ [bp+6] ; define parameter addressing
- ycoord equ [bp+8]
- pcolor equ [bp+10]
-
- ELSE
- _pset proc near
-
- xcoord equ [bp+4]
- ycoord equ [bp+6]
- pcolor equ [bp+8]
- ENDIF
-
- push bp
- mov bp,sp
-
- jmp [psetjump] ; transfer control to appropriate
- ; point-plotting routine
-
- psetexit: ; control is transferred here if
- ; setpjmp() was never called or the
- ; video display is currently in a
- ; text mode
- pop bp
- ret
-
- pset320: ; plot a point in 320x200 4-color mode
- mov bx,ycoord ; get y coordinate
- xor dx,dx
- shr bx,1
- rcr dx,1 ; rotate low bit of bx into bit 13
- ror dx,1 ; of dx
- ror dx,1 ; dx=2000h if y is odd, =0 if y is even
- mov cl,4
- shl bx,cl ; bx=(hi 7 bits of y coordinate) * 16
- mov ax,bx
- shl bx,1
- shl bx,1 ; bx=y coordinate * 64
- add bx,ax ; bx=y coordinate * 80
- add bx,dx ; bx=y coordinate * 80 + major offset
- mov ax,xcoord ; get x coordinate
- mov cx,ax
- shr ax,1
- shr ax,1
- add bx,ax ; bx now contains video memory offset
- mov dl,pcolor ; get color
- and dl,3 ; only low 2 bits are used in 4-color
- ; mode
- not cl
- and cl,3 ; cl=inverted low 3 bits of x coord.
- ; this is used as a shift count to
- ; position the 2-bit color value
- ; within the proper byte in video RAM
- add cl,cl
- shl dl,cl
- mov dh,255-3
- rol dh,cl ; dh=mask ("and") value, dl="or" value
- push es
- mov es,_vidmseg
- and es:[bx],dh
- or es:[bx],dl
- pop es
- pop bp
- ret
-
- pset640: ; plot a point in 640x200 2-color mode
- mov bx,ycoord ; get y coordinate
- xor dx,dx
- shr bx,1
- rcr dx,1
- ror dx,1
- ror dx,1 ; dx=2000h if y is odd, =0 if y is even
- mov cl,4
- shl bx,cl
- mov ax,bx
- shl bx,1
- shl bx,1
- add bx,ax
- add bx,dx
- mov ax,xcoord ; get x coordinate
- mov cx,ax
- shr ax,1
- shr ax,1
- shr ax,1
- add bx,ax
- mov dl,pcolor ; get color
- and dl,1 ; only low bit of color is significant
- not cl
- and cl,7
- shl dl,cl
- mov dh,254
- rol dh,cl
- push es
- mov es,_vidmseg
- and es:[bx],dh
- or es:[bx],dl
- pop es
- pop bp
- ret
-
- pset160: ; plot a point in 160x100 16-color mode
- mov bx,ycoord ; get y coordinate
- mov cx,bx
- shl cx,1
- shl cx,1
- add bx,cx ; bx=y coordinate * 5
- shl bx,1
- shl bx,1
- shl bx,1
- shl bx,1
- shl bx,1 ; bx = y coordinate * 160
- mov ax,xcoord ; get x coordinate
- mov cx,ax
- and ax,0fffeh ; mask off low bit
- add bx,ax ; bx now contains video memory offset
- and cx,1 ; low bit of x coordinate determines
- ; whether foreground or background
- ; attribute of character will be set
- shl cl,1
- shl cl,1 ; cl=0 for even x coordinates (left
- ; half of character position -
- ; foreground will be set), 4 for odd
- mov dl,pcolor ; get color
- and dl,15 ; use only low 4 bits of color
- shl dl,cl
- mov dh,240
- rol dh,cl ; dh=mask ("and") value, dl="or" value
- push es
- mov es,_vidmseg
- mov cl,221
- mov ch,es:[bx+1] ; get current video RAM byte
- and ch,dh ; modify it
- or ch,dl
- mov es:[bx],cx ; store 221 character with new
- ; attribute in video memory
- pop es
- pop bp
- ret
- _pset endp
-
- ; The following function performs the special CRT controller initialization
- ; for the 160x100 graphics modes:
-
- IF CODE_M
- _init160 proc far ; init160()
- ELSE
- _init160 proc near
- ENDIF
-
- mov dx,3d8h
- mov al,9
- out dx,al ; disable blinking attribute bit,
- ; enabling 16 background colors
- mov dx,3d4h ; CRT controller's base I/O address
- mov al,4 ; register 4 = total vertical rows
- ; per frame
- out dx,al
- inc dx
- mov al,127 ; 127 rows total
- out dx,al
- dec dx
- mov al,6 ; register 6 = total displayed rows
- out dx,al
- inc dx
- mov al,100 ; 100 rows will be displayed. There is
- ; actually enough memory for 102 rows
- ; if your monitor can display the
- ; extra eight scan lines
- out dx,al
- dec dx
- mov al,7 ; VSYNC position register
- out dx,al
- inc dx
- mov al,112 ; VSYNC position - don't change
- out dx,al
- dec dx
- mov al,9 ; # of scan lines per row register
- out dx,al
- inc dx
- mov al,1 ; register 9 actually contains the
- ; number of scan lines per row minus
- ; 1, so for 2 scan lines per row a
- ; value of 1 is stored
- out dx,al
- push es
- mov ax,40h
- mov es,ax
- mov bx,_mode
- mov byte ptr es:[49h],bl ; set BIOS current video mode byte
- mov es,_vidmseg
- xor di,di
- mov cx,16384
- mov ax,00ddh ; set all characters to display
- ; character 221 with an attribute
- ; byte of zero (black foreground &
- ; background)
- rep stosw ; clear screen
- pop es
- ret
- _init160 endp
-
- ; The following routine stores in [psetjump] the address of the
- ; point-plotting routine appropriate for the current video mode:
-
- IF CODE_M
- _setpjmp proc far ; setpjmp(video_mode)
-
- mode equ [bp+6] ; define parameter addressing
-
- ELSE
- _setpjmp proc near
-
- mode equ [bp+4]
- ENDIF
-
- push bp
- mov bp,sp
-
- mov ax,mode
- shr ax,1
- cmp ax,2 ; 320x200 b/w or color mode?
- mov bx,offset _TEXT:pset320
- je setpj1
- cmp ax,3 ; 640x200 b/w mode?
- mov bx,offset _TEXT:pset640
- je setpj1
- cmp ax,4 ; 160x100 b/w or color mode?
- mov bx,offset _TEXT:pset160
- je setpj1
- ; for non-graphics modes or unsupported
- ; modes, use "psetexit" routine which
- ; does nothing
- mov bx,offset _TEXT:psetexit
- setpj1: mov psetjump,bx ; set address of point-plotting routine
- pop bp
- ret
- _setpjmp endp
-
- ; D R A W A L I N E
- ;
- ; This routine draws a line between points (x1,y1) and (x2,y2) with
- ; the given color. It supports whatever graphics modes are
- ; supported by the pset() routine and should work with any future
- ; graphics modes up to 65536x65536 resolution.
- ;
- ; The basic idea behind this function's operation is this: It computes
- ; "delta x" which equals the difference between x2 and x1, and "delta y"
- ; which equals the difference between y2 and y1. It starts drawing
- ; the line at coordinates (x1, y1) on the screen. It then adds 1/65536
- ; of delta x to the current x coordinate and 1/65536 of delta y to the
- ; current y coordinate to find the coordinates of the next point, draws the
- ; next point and repeats. The effect is that the slope of the line connecting
- ; any two adjacent points it draws is equal to the slope of the line
- ; connecting (x1, y1) and (x2, y2) and thus it draws a straight line from
- ; (x1, y1) to (x2, y2). By the time it has looped 65536 times it has added
- ; 65536/65536ths of delta x to x and 65536/65536ths of delta y to y and thus
- ; has reached the coordinates (x2, y2), the end of the line. Of course,
- ; modifications to this algorithm have been made so the routine doesn't
- ; actually plot 65536 dots for every line it draws; in fact, it doesn't
- ; ever draw a dot at a location where it has already drawn one.
- ;
- ; The CPU's registers are used as follows:
- ; DX - current x coordinate
- ; AX - current fraction of x coordinate in 65536ths
- ; CX - current y coordinate
- ; BX - current fraction of y coordinate in 65536ths
- ; SI - delta x (when x1 > x2, SI=absolute value of delta x)
- ; DI - delta y (always positive - see below)
- ;
-
- IF CODE_M
- _line proc far ; line(x1, y1, x2, y2, color)
-
- x1 equ [bp+6] ; define parameter addressing
- y1 equ [bp+8]
- x2 equ [bp+10]
- y2 equ [bp+12]
- color equ [bp+14]
-
- ELSE
- _line proc near
-
- x1 equ [bp+4]
- y1 equ [bp+6]
- x2 equ [bp+8]
- y2 equ [bp+10]
- color equ [bp+12]
- ENDIF
-
- push bp
- mov bp,sp ; set up parameter addressing on stack
- push si ; must be preserved for MSC
- push di
-
- mov count,0 ; initialize count for later
- mov cx,y1 ; get y1 coordinate
- mov di,y2 ; get y2 coordinate
- sub di,cx ; di=delta y
- jnc line1
- mov ax,x1 ; if y1 > y2, swap x1 with x2 and
- ; y1 with y2 so that y1 is always
- ; less than y2
- mov bx,x2
- mov x1,bx
- mov x2,ax
- mov ax,y1
- mov bx,y2
- mov y1,bx
- mov y2,ax ; swap x & y coords. so that
- ; x2<x1, y2>y1
- mov cx,bx ; get y1 coordinate
- mov di,ax ; get y2 coordinate again
- sub di,cx ; di=delta y, is positive this time
- line1: mov bx,0ffffh ; for later
- mov dx,x1 ; get x1 coordinate
- mov ax,0ffffh ; for later
- mov si,x2 ; get x2 coordinate
- sub si,dx ; si=delta x
- jc line10 ; if x1 > x2, draw the line with x
- ; decreasing from x1 to x2. Otherwise,
- ; draw the line with x increasing from
- ; x1 to x2. In either case, y
- ; increases from y1 to y2.
- or si,si ; first, multiply delta x and delta y
- ; by 2 until the high bit of one or
- ; both is a 1 (normalize delta x and
- ; delta y)
- js line3 ; if either delta x or delta y is
- ; already normalized, skip the
- ; following code; count is already
- ; initialized to zero (which means
- ; an actual count of 65536 pixels will
- ; be drawn)
- or di,di
- js line3
- shl si,1
- shl di,1
- mov count,8000h ; divide count by 2 each time dx and
- ; dy are doubled
- line2: or si,si
- js line3
- or di,di
- js line3
- shl si,1
- shl di,1
- shr count,1 ; divide count by 2
- jnz line2 ; continue unless count is now zero
- mov count,1 ; if count is zero, (x1, y1) and
- ; (x2, y2) must be the same point;
- ; set count equal to 1
- jmp line3
- line3a: add bx,di
- jnc line4
- inc cx
- line3: push ax ; main loop
- push bx
- push di ; save registers that will be destroyed
- ; by the pset() routine
- push color ; pass color to pset()
- push cx ; pass y coordinate and
- push dx ; x coordinate to pset() and also
- ; save them on the stack
- call _pset ; draw next point on the line
- pop dx ; restore dx & cx from stack (pset()
- pop cx ; doesn't modify the parameters it
- ; gets on the stack so it is safe to
- ; do this)
- add sp,2 ; adjust stack
- pop di ; restore registers
- pop bx
- pop ax
- line4: dec count
- jz line5 ; if count is zero, exit
- add ax,si ; otherwise, adjust x & y coordinates
- ; for next point on the line
- jnc line3a ; line3a loops back to line4 if
- ; neither the x nor the y coordinate
- ; was changed by the adjustment to
- ; save the time of redrawing a dot at
- ; the same place that one was just
- ; drawn
- inc dx ; if ax overflowed, add carry into dx
- add bx,di ; adjust y coordinate
- adc cx,0 ; add in carry, if any
- jmp line3
- line5: pop di
- pop si
- pop bp
- ret
-
- line10: ; This is the same code as is at
- ; line1 above, except that it
- ; subtracts the si register from the
- ; current x coordinate rather than
- ; adding it; this routine is used if
- ; x1 > x2 while the code at line1 is
- ; used if x1 < x2. No matter what,
- ; y1 is always less than or equal
- ; to y2.
- neg si ; now both si and di are positive
- ; (actually unsigned 16-bit integers)
- or si,si
- js line13
- or di,di
- js line13
- shl si,1
- shl di,1
- mov count,8000h
- line12: or si,si
- js line13
- or di,di
- js line13
- shl si,1
- shl di,1
- shr count,1
- jnz line12
- mov count,1
- jmp line13
-
- line13a:add bx,di
- jnc line14
- inc cx
- line13: push ax
- push bx
- push di ; save registers destroyed by pset()
- push color ; pass color,
- push cx ; y coordinate and
- push dx ; x coordinate to pset()
- call _pset
- pop dx
- pop cx
- add sp,2
- pop di
- pop bx
- pop ax
- line14: dec count
- jz line15
- sub ax,si ; subtract x coordinate adjustment
- jnc line13a
- dec dx ; if there was a borrow from the MSB
- ; of x coordinate, decrement it by one
- add bx,di ; now adjust the y coordinate
- adc cx,0
- jmp line13
-
- line15: pop di
- pop si
- pop bp
- ret
- _line endp
-
- ; F A S T L I N E - D R A W
- ;
- ; This routine operates on the same basic principle as the line-drawing
- ; function above, but differs in that it manipulates video RAM contents
- ; directly instead of going through the pset() routine. It is roughly
- ; five times as fast as the routine above, but only works in the 320x200
- ; and 640x200 graphics modes. There are four sections of code in this
- ; function, only one of which will actually be executed on a particular
- ; call to the routine:
- ; lin0 - used if x1 >= x2 and delta y >= delta x
- ; lin10 - used if x1 >= x2 and delta y < delta x
- ; lin20 - used if x1 < x2 and delta y >= delta x
- ; lin30 - used if x1 < x2 and delta y < delta x
- ;
- ; Hopefully soon I will have time to clean this code up a little
- ; and document it better, but for the meantime, at least it runs real
- ; fast.
-
- IF CODE_M
- _fline proc far ; fline()
- ELSE
- _fline proc near
- ENDIF
-
- ;x1 equ [bp+4] ; these five parameters were defined in
- ;y1 equ [bp+6] ; the line() routine above and are the
- ;x2 equ [bp+8] ; same for this routine; they are commented
- ;y2 equ [bp+10] ; out because MASM does not allow redefinition
- ;color equ [bp+12] ; of equ's
-
- push bp
- mov bp,sp ; set up parameter addressing on stack
- push si ; must be preserved for MSC
- push di
-
- lin0c: push color
- push y1
- push x1
- call _pset ; pset() returns bx=video memory offset,
- ; dh=mask value, dl=color value of the
- ; plotted point
- add sp,6 ; remove parameters
- mov di,bx ; place video offset in di
- mov cx,y2
- sub cx,y1 ; compute delta y
- jnc lin0b
- mov ax,x1 ; if y1 > y2, swap x1 with x2 and y1 with y2
- mov bx,x2 ; so that y1 is always less than y2
- mov x1,bx
- mov x2,ax
- mov ax,y1
- mov bx,y2
- mov y1,bx
- mov y2,ax
- jmp lin0c ; go back and recompute delta y, etc.
-
- lin0b: push es
- mov es,_vidmseg
- push dx
- mov dx,x2
- sub dx,x1 ; get delta x in dx
- jnc lin0d
- neg dx ; if delta x < 0, use routine at lin20
- jmp lin20
- lin0d: cmp dx,cx ; is delta x > delta y?
- ja lin10 ; if yes, use routine at lin10
- jb lin0
- or cx,cx ; if delta x and delta y are both zero, exit
- jz lin0e
- mov ax,0ffffh ; otherwise, delta x = delta y; set up ax
- ; and bypass divide
- jmp short lin0a
-
- lin0e: pop dx ; remove dx from stack
- jmp flindone ; exit
-
- lin0: xor ax,ax ; dx:ax contains delta x * 65536
- div cx ; compute 65536 * delta x / delta y
- lin0a: mov adj,ax ; save 65536 * delta x / delta y
- pop dx
- mov si,2000h
- test byte ptr y1,1 ; is y1 even?
- jz lin1 ; if so, 2000h is initial video memory
- ; address adjust
- mov si,0e050h ; if not, e050h is
- lin1: mov bx,8000h ; initialize 65536ths counter for x
- mov bp,adj ; due to lack of registers, we're forced to
- ; use bp to hold x adjust value
-
- lin2: ; main loop
- add di,si ; add 2000h or e050h alternately to video
- ; memory offset
- xor si,0c050h ; toggle 2000h to e050h and back
- add bx,bp ; adjust x
- jc lin4 ; if low word of x coordinate overflowed,
- ; do lin4
- lin3: mov al,es:[di] ; get current video memory byte
- and al,dh ; modify it
- or al,dl
- stosb ; store new byte in video RAM to plot point
- ; on line
- dec di
- loop lin2
- jmp flindone
- lin4: ; here we adjust the mask ("and") and "or"
- ; values used to modify video memory contents
- ; to reflect the change in the x coordinate
- mov ax,cx
- mov cx,_bitspixl
- lin5: ror dl,cl
- ror dh,cl
- mov cx,ax
- jc lin3 ; if a bit of 1 was shifted through the carry
- ; flag when rotating dh, then di doesn't need
- ; to be adjusted
- inc di ; adjust di to point to next location to the
- ; right in video memory
- jmp short lin3
-
- lin10:
- xor ax,ax
- xchg cx,dx ; place delta x in cx and delta y in dx
- div cx
- mov adj,ax ; put 65536 * delta x / delta y in "adj"
- pop dx
- mov si,2000h
- test byte ptr y1,1
- jz lin11
- mov si,0e050h
- lin11: mov bx,8000h
- mov bp,adj ; place y adjust in bp
- mov ax,cx
- mov cx,_bitspixl
- jmp lin12
- lin12a: inc di
- jmp lin13
- lin12: ror dl,cl
- ror dh,cl
- jnc lin12a
- lin13: add bx,bp ; adjust y
- jc lin14
- lin13a: mov ch,es:[di]
- and ch,dh
- or ch,dl
- mov es:[di],ch
- dec ax
- jnz lin12
- jmp flindone
- lin14: add di,si ; add 2000h or e050h alternately to video memory offset
- xor si,0c050h ; toggle 2000h to e050h and back
- jmp lin13a
-
- lin20: cmp dx,cx ; is delta x > delta y?
- ja lin30
- jb lin20b
- mov ax,0ffffh ; if delta x = delta y, set up ax and bypass divide
- jmp lin20a
- lin20b: xor ax,ax
- div cx
- lin20a: mov adj,ax ; put 65536 * delta x / delta y in "adj"
- pop dx
- mov si,2000h
- test byte ptr y1,1 ; is y1 even?
- jz lin21 ; if so, 2000h is initial address adjust
- mov si,0e050h ; if not, this is
- lin21: mov bx,8000h ; initialize 65536ths counter for x
- mov bp,adj ; place x adjust in bp
- lin22: add di,si ; add 2000h or e050h alternately to video memory offset
- xor si,0c050h ; toggle 2000h to e050h and back
- add bx,bp ; adjust x
- jc lin24
- lin23: mov al,es:[di]
- and al,dh
- or al,dl
- stosb
- dec di
- loop lin22
- jmp flindone
- lin24: mov ax,cx
- mov cx,_bitspixl
- lin25: rol dl,cl
- rol dh,cl
- mov cx,ax
- jc lin23 ; dec instruction doesn't affect carry flag
- dec di
- jmp lin23
-
- lin30: xor ax,ax
- xchg cx,dx ; place delta x in cx and delta y in dx
- div cx
- mov adj,ax ; put 65536 * delta x / delta y in "adj"
- pop dx
- mov si,2000h
- test byte ptr y1,1
- jz lin31
- mov si,0e050h
- lin31: mov bx,8000h
- mov bp,adj ; place y adjust in bp
- mov ax,cx
- mov cx,_bitspixl
- jmp lin32
- lin32a: dec di
- jmp lin33
- lin32: rol dl,cl
- rol dh,cl
- jnc lin32a
- lin33: add bx,bp ; adjust y
- jc lin34
- lin33a: mov ch,es:[di]
- and ch,dh
- or ch,dl
- mov es:[di],ch
- dec ax
- jnz lin32
- jmp flindone
- lin34: add di,si ; add 2000h or e050h alternately to video
- ; memory offset
- xor si,0c050h ; toggle 2000h to e050h and back
- jmp lin33a
- flindone:
- pop es
- pop di
- pop si
- pop bp
- ret
- _fline endp
-
- ; The function below calls the specified interrupt number after loading
- ; the specified value into the AX register.
-
- IF CODE_M
- _doint proc far ; doint(intno, r_ax)
-
- intno equ [bp+6] ; define parameter addressing
- r_ax equ [bp+8]
-
- ELSE
- _doint proc near
-
- intno equ [bp+4]
- r_ax equ [bp+6]
- ENDIF
-
- push bp
- mov bp,sp
-
- push es
- xor ax,ax
- mov es,ax
- mov bl,intno ; put interrupt # in al
- xor bh,bh
- shl bx,1
- shl bx,1 ; es:[bx] now contains interrupt vector
- mov ax,r_ax ; load variable "ax" into ax
- pushf ; set up stack for iret instruction
- ; that the interrupt routine will
- ; perform
- call dword ptr es:[bx]
- pop es
-
- pop bp
- ret
- _doint endp
-
- _TEXT ends
- end